﻿using System;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using Avanzis.QueryFramework;

namespace Avanzis.QueryBuilder.Fluent
{
    /// <summary>
    ///   Helper to create sql queries in a fluent way
    /// </summary>
    public static class FluentHelper
    {
       

        
        /// <summary>
        /// </summary>
        /// <param name="property"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        public static string ParseProperty<T>(Expression<Func<T, object>> property)
        {
            if (property.Body is MemberExpression)
            {
                string fullName = property.Body.ToString();

                fullName = fullName.Substring(fullName.IndexOf(".", StringComparison.Ordinal) + 1);

                return fullName.Replace(".", QueryFrameworkConfiguration.Conventions.SubPropertySeparator);
            }
            else
            {
                var operand = (((UnaryExpression) property.Body).Operand as MemberExpression);

                if (operand == null)
                {
                    return null;
                }

                string fullName = operand.ToString();

                fullName = fullName.Substring(fullName.IndexOf(".", StringComparison.Ordinal) + 1);

                return fullName.Replace(".", QueryFrameworkConfiguration.Conventions.SubPropertySeparator);
            }
        }

        private static readonly ITableNamePluralizer _defaultTableNamePluralizer = new DefaultTablePluralizer();

        /// <summary>
        ///   Gets the table name
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        public static string GetTableName<T>()
        {
            string tableName = typeof(T).Name;
            if (!QueryFrameworkConfiguration.Conventions.PluralizeTables)
            {
                return tableName;
            }

            var pluralizer = QueryFrameworkConfiguration.Conventions.TableNamePluralizer ?? _defaultTableNamePluralizer;
            return pluralizer.Pluralize(tableName);
        }

      

        /// <summary>
        ///   Gets a SQLField from a string
        /// </summary>
        /// <param name="expression"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        public static SqlField GetField<T>(string expression)
        {
            SqlField field = new SqlField();
            field.ColumnNameOrExpression = expression;
            field.TableName = GetTableName<T>();
            return field;
        }

        /// <summary>
        ///   Gets a SQLField from expression
        /// </summary>
        /// <param name="property"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        public static SqlField GetField<T>(Expression<Func<T, object>> property)
        {
            return GetField(property, true);
        }

        /// <summary>
        ///   Gets a SQLField from expression that includes the table name
        /// </summary>
        /// <param name="property"> </param>
        /// <param name="includeTableName"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        public static SqlField GetField<T>(Expression<Func<T, object>> property, bool includeTableName)
        {
            SqlField field = new SqlField();
            field.ColumnNameOrExpression = ParseProperty(property);
            if (includeTableName)
            {
                field.TableName = GetTableName<T>();
            }
            return field;
        }

        /// <summary>
        ///   Gets a SQLField from expression with an alias
        /// </summary>
        /// <param name="propertyExpression"> </param>
        /// <param name="aliasExpression"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <typeparam name="TK"> </typeparam>
        /// <returns> </returns>
        public static SqlField GetField<T, TK>(Expression<Func<T, object>> propertyExpression, Expression<Func<TK, object>> aliasExpression)
        {
            SqlField field = new SqlField();
            field.ColumnNameOrExpression = ParseProperty(propertyExpression);
            field.TableName = GetTableName<T>();
            field.Alias = ParseProperty(aliasExpression);
            return field;
        }

        /// <summary>
        ///   Gets a SQLField from expression with an alias
        /// </summary>
        /// <param name="propertyExpression"> </param>
        /// <param name="aliasExpression"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <typeparam name="TK"> </typeparam>
        /// <returns> </returns>
        public static SqlField GetField<T, TK>(string propertyExpression, Expression<Func<TK, object>> aliasExpression)
        {
            SqlField field = new SqlField();
            field.ColumnNameOrExpression = propertyExpression;
            field.TableName = GetTableName<T>();
            field.Alias = ParseProperty(aliasExpression);
            return field;
        }

        /// <summary>
        ///   Gets a SQLField from expression
        /// </summary>
        /// <param name="propertyExpression"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <typeparam name="TK"> </typeparam>
        /// <returns> </returns>
        public static SqlField GetField<T, TK>(string propertyExpression)
        {
            SqlField field = new SqlField();
            field.ColumnNameOrExpression = propertyExpression;
            field.TableName = GetTableName<T>();
            return field;
        }

        /// <summary>
        ///   Gets the parameter name from the expression
        /// </summary>
        /// <param name="propertyExpression"> </param>
        /// <typeparam name="T"> </typeparam>
        /// <returns> </returns>
        public static string GetParameterName<T>(Expression<Func<T, object>> propertyExpression)
        {
            var parameterName = ParseProperty(propertyExpression);

            return parameterName;
        }
    }
}